; Windows 32-bit CTF challenge
;   spipm, BraekerCTF 2024
;
;   > Thread bytecode in PE header
;     TinyPE credit to Alexander Sotirov:
;       http://www.phreedom.org/research/tinype/
;
;   1. Resolves functions from kernel32 via PEB
;     1.1. Also checks IsBeingDebugged
;   2. Creates new thread
;   3. New thread xors buffer_a with 0x49 + (32-i)
;     3.1. Starts sleeping (using Beep)
;   4. First thread xors buffer_a with cmd line args
;   5. First thread checks if buffer_a matches buffer_b
;
;

BITS 32

%define round(n, r) (((n+(r-1))/r)*r)

mzhdr:
;------
  CODE_MZ:

    dw "MZ"         ; dec ebp, pop edx

    push edx
    ret

;------


pesig:
  ; e_cp, e_crlc UNUSED / PE signature
  dd "PE"
pehdr:
  ; e_cparhdr / Machine (Intel 386)
  dw 0x014C
  ; e_minalloc UNUSED / NumberOfSections
  dw 1          

;------
  threadfunction_start:
  ; 12 byte space

    ; xor buffer met 0x49
    xor ecx, ecx
    add cl, xorbuffer_size     ; Loop counter
    mov esi, 0xf00000 + buffer_a      ; Address of the array 
    manbufloop_start:
      jmp CODE_OPTHDR

  threadfunction_start_size equ $ - threadfunction_start
  times (12 - threadfunction_start_size) db 0xff

;------

  ; e_lsarlc UNUSED / SizeOfOptionalHeader
  dw opthdrsize 
  ; e_ovno UNUSED / Characteristics
  dw 0x103      
opthdr:
  ; e_res UNUSED / Magic (PE32)
  dw 0x10B      

;------
    CODE_OPTHDR:
    ; 14 byte space

      ; code
      ; rest van loop
      xor byte [esi], 0x49  ; XOR accumulator with the value at [esi]
      add [esi], ecx
      jmp CODE_OPTHDR2
      
    ; [distraction]
    ; 6 bytes: confusing xor statements
    xor byte [esi], 0x32
    inc esi
    jmp confuse_loop

    CODE_OPTHDR_size equ $ - CODE_OPTHDR
    times (14 - CODE_OPTHDR_size) db 0xff
;------

    dd code                                  ; AddressOfEntryPoint
    dd code                                   ; BaseOfCode UNUSED
    dd round(filesize, 4)             ; BaseOfData UNUSED
    dd 0xf00000                               ; ImageBase
    dd 4  ; e_lfanew                  ; SectionAlignment
    dd 4                              ; 4ment

;------
    CODE_OPTHDR2:
    ; 8 byte space

      ; code
      ; laatste stukje loop
      inc esi             ; Increment the array address
      loop manbufloop_start     ; Loop until ecx becomes zero

      jmp beep


    CODE_OPTHDR2_size equ $ - CODE_OPTHDR2
    times (8 - CODE_OPTHDR2_size) db 0xff

;------

    db 0x04, 0x00                                      ; MajorSubsystemVersion
    dw 0                                      ; MinorSubsystemVersion UNUSED
    dd 0                                      ; Win32VersionValue UNUSED
    dd round(filesize, 4)             ; SizeOfImage
    dd round(hdrsize, 4)              ; SizeOfHeaders
    dd 0x1337                                      ; CheckSum UNUSED
    dw 3                                      ; Subsystem (Win32 GUI)
    dw 0x400                                  ; DllCharacteristics UNUSED
    dd 0x100000                               ; SizeOfStackReserve
    dd 0x1000                                 ; SizeOfStackCommit
    dd 0x100000                               ; SizeOfHeapReserve
    dd 0x1000

;------
  SPACE_24:
    ; 24 byte space

    ; [distraction]
    ; just a bunch of confusion bytecode
    confuse_loop:
      mov esi, 0xf00000 + 98
      xor [esi], eax
      inc esi
      shl eax, 2
      xor [esi], eax
      jne threadfunction_start
      db 0x77 ; lol
      mov esi, 0xf00000 + 99
      jmp confuse_loop

  SPACE_24_size equ $ - SPACE_24
  times (24 - SPACE_24_size) db 0xff


;------

db 0x00, 0x00, 0x00, 0x00  ; resource directory, must be 'legit'

;------
  resolve_addresses:
  ; 52 BYES SPACE

    ; Store number of elements
    sub ecx, ecx
    add ecx, (hash_list_size/4) 
    mov ebp, ecx

    ; Store starting address
    mov ebx, 0xf00000+hashes_of_kernel32_functions+(hash_list_size-4)

    loop_resolve:
        ; Load the address from the list
        xor eax, eax
        mov al, [0xf00000+function_nameSizes+ebp-1]
        push eax
        mov eax, [ebx]
        push eax
        
        ; Call your function
        call getAddressOfKernel32FunctionByHash
        ; Store resolved address
        mov [ebx], eax

        ; Decrease counter with gadget
        ; dec ebp, pop edx, push edx, ret
        call CODE_MZ 
        ; clear stack
        pop edx
        pop ecx

        ; Increment pointer, decrease counter
        sub ebx, 4
        mov ecx, ebp
        inc ecx
        dec ecx
        jnz loop_resolve


    pop ebx
    jmp ebx

    ; fill space
    db 0x00, 0x00

    resolve_addresses_size equ $ - resolve_addresses
    times (52 - resolve_addresses_size) db 0x77

;------

db 0x00, 0x00, 0x00, 0x00 ; TLS directory address?

;------
  CODE_DIR3:

    ; [distraction]
    jmp CODE_DIR4
    jmp loop_resolve

  CODE_DIR3_size equ $ - CODE_DIR3
  times (4 - CODE_DIR3_size) db 0x66
;------

db 0x00, 0x00, 0x00, 0x00 ; Load config directory address?

;------
  CODE_DIR4:
  ; 12 byte space

    ; code
    ; [distraction]
    db 0x77 ; jmp
    db 0x90, 0x00 ; some nullbytes
    mov esi, [0xf00000+getAddressOfKernel32FunctionByHash+13]
    call esi
    ret

  CODE_DIR4_size equ $ - CODE_DIR4
  times (12 - CODE_DIR4_size) db 0x77
;------

db 0x00, 0x00, 0x00, 0x00 ; IAT address?

;------
  CreateThread_start:

    ; code
    ; Call CreateThread
    ; 9 bytes
    mov esi, [0xf00000+CreateThread]
    xor eax, eax
    xor ecx, ecx
    ; 2 bytes
    jmp CreateThread_2

  CreateThread_start_size equ $ - CreateThread_start
  times (12 - CreateThread_start_size) db 0xff
;------

db 0x00, 0x00, 0x00, 0x00 ; ?

;------
  CreateThread_2:

    ; code
    ; 10 bytes
    add   ecx, 0x6                ; 6 dwords
    lea   edi, [esp]              ; Start address ([esp])
    rep   stosd                   ; Clear the memory range
    mov al, 0xf
    ; 2 bytes
    jmp CreateThread_3

  CreateThread_2_size equ $ - CreateThread_2
  times (12 - CreateThread_2_size) db 0xff
;------


opthdrsize equ $ - opthdr

db ".code", 0, 0, 0                       ; Name
dd codesize                               ; VirtualSize
dd round(hdrsize, 4)              ; VirtualAddress
dd round(codesize, 4)             ; SizeOfRawData
dd code                                   ; PointerToRawData

;------
  CreateThread_3:
  ; 16 byte space

    ; code
    ; 13 bytes
    shl eax, 20
    add ax, threadfunction_start
    mov     DWORD [esp+0x8], eax   ; Store the result in DWORD [esp+0x8]
    call   esi
    ; 2 bytes
    jmp continue ; has to be close jump! Else it takes too much bytes

  CreateThread_3_size equ $ - CreateThread_3
  times (16 - CreateThread_3_size) db 0xff
;------


hdrsize equ $ - $$

;------
  CODE_ENTRYPOINT:


code:
  
  ; Resolve addresses from hash to kernel32 handles
  call resolve_addresses

  ; Create second thread
  jmp CreateThread_start
  continue:

  ; [TO OBFUSCATE START]

  ; Print first msg to stdout
  push -11
  call [0xf00000 + GetStdHandle]
  sub    esp,0x3c
  mov    DWORD  [esp+0x14],0
  mov    DWORD  [esp+0x10],0
  mov    DWORD  [esp+0xc],0
  mov    DWORD  [esp+0x8],22
  mov    DWORD  [esp+0x4], 0xf00000 + didyou_msg
  mov    DWORD  [esp],eax
  call [0xf00000 + WriteFile]

  ; Wait for second thread to change buffer
  ; call Beep
  mov    DWORD  [esp+0x4],0xfa0
  mov    DWORD  [esp],0x320
  mov    ecx, eax
  call   [0xf00000 + Beep]

  ; xor buffer with cmdline
  ; call GetCommandLineA
  call   [0xf00000 + GetCommandLineA]
  add eax, 16 ; skip filename
  ; xor buffer
  mov ecx, xorbuffer_size          ; Loop counter (number of array elements)
  mov esi, 0xf00000 + buffer_a      ; Address of the array 
  loop_start:
      mov edx, [eax]
      xor byte [esi], dl  ; XOR accumulator with the value at [esi]
      inc esi             ; Increment the array address
      inc eax
      loop loop_start     ; Loop until ecx becomes zero

  ; check if buffer matches other buffer
  CompareBuffers:
      mov esi, 0xf00000+buffer_a  ; First buffer pointer
      mov edi, 0xf00000+buffer_b ; Second buffer pointer
      mov ecx, xorbuffer_size ; Number of bytes to compare

      xor eax, eax      ; Clear EAX register (used for result)

  compare_loop:
      mov al, [esi]     ; Load byte from first buffer
      cmp al, [edi]     ; Compare with byte from second buffer
      jne unequal       ; Jump if not equal

      inc esi           ; Increment first buffer pointer
      inc edi           ; Increment second buffer pointer
      dec ecx           ; Decrement byte count

      jnz compare_loop  ; Jump if byte count is not zero

  equal:
    ;xor eax, eax      ; Set EAX to 0 (buffers are not equal)
    mov eax, 0xf00000 + msg_flag
    mov dword [eax], 0x00736579 ; 'yes\0'

    ; [TODO play some nice beeps here]

    jmp end_compare
    
  unequal:
  end_compare:

    ; call GetStdHandle
    push -11
    call [0xf00000 + GetStdHandle]
    mov ebx, eax ; Store the stdout handle

    ; call WriteFile 
    sub    esp,0x3c
    mov    DWORD  [esp+0x14],0
    mov    DWORD  [esp+0x10],0
    mov    DWORD  [esp+0xc],0
    mov    DWORD  [esp+0x8],3
    mov    DWORD  [esp+0x4], 0xf00000 + msg_flag
    mov    DWORD  [esp],ebx
    call [0xf00000 + WriteFile]

  ; wait for the beep
  beep:
  mov    DWORD  [esp+0x4],0x1337 ; ~5 sec
  mov    DWORD  [esp],0x320
  call   [0xf00000 + Beep]

  ; [TO OBFUSCATE END]

  ; ret into some fault to quit
  ret



hashes_of_kernel32_functions:

  ; hashes that are to be resolved so they can be used in the code
  Beep:
    dd 0x54ee8e5
  WriteFile:
    dd 0x1b35dcf5
  GetStdHandle:
    dd 0x274362e2
  GetCommandLineA:
    dd 0x8c5ca57
  CreateThread:
    dd 0x34d3d2f8

hash_list_size equ $ - hashes_of_kernel32_functions

function_nameSizes:
  
  ; sizes of function names 
  db 4 ; Beep
  db 9 ; WriteFile
  db 12 ; GetStdHandle
  db 15 ; GetCommandLineA
  db 12 ; CreateThread


; Resolve Kernel32 function via PEB
; also has 2 checks for IsBeingDebugged
  ; -> if being debugged it a) skips kernel32 and b) 
  ;  if (rollingHash((unsigned char*)pEntry->BaseDllName.Buffer, 24) == KERNEL32HASH && ProcEnvBlk->BeingDebugged == 0) {
  ;     [...]
  ;     if (rollingHash((unsigned char *) pBaseAddr + (DWORD_PTR) pFuncNameTbl[i], len) == hash) {
  ;       return (DWORD *)(pBaseAddr + ProcEnvBlk->BeingDebugged + (DWORD_PTR) pEAT[pHintsTbl[i]]);
; getAddressOfKernel32FunctionByHash(name_hash, name_len)
getAddressOfKernel32FunctionByHash:
; Without IsBeginDebugged check:
;db 0x55, 0x57, 0x56, 0x53, 0x83, 0xec, 0x18, 0x64, 0xa1, 0x30, 0x00, 0x00, 0x00, 0x8b, 0x40, 0x0c, 0x8d, 0x78, 0x14, 0x8b, 0x40, 0x14, 0x89, 0x7c, 0x24, 0x10, 0x89, 0x04, 0x24, 0x39, 0xc7, 0x74, 0x60, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x24, 0xba, 0x49, 0x00, 0x00, 0x00, 0xbe, 0x03, 0x00, 0x00, 0x00, 0x8b, 0x58, 0x28, 0x8d, 0x7b, 0x18, 0x8d, 0x74, 0x26, 0x00, 0x90, 0x0f, 0xb6, 0x0b, 0x83, 0xc3, 0x01, 0x01, 0xd1, 0xc1, 0xe2, 0x05, 0x01, 0xd1, 0x89, 0xc8, 0xf7, 0xe6, 0x89, 0xc8, 0x29, 0xd0, 0xd1, 0xe8, 0x01, 0xc2, 0xc1, 0xea, 0x1e, 0x89, 0xd0, 0xc1, 0xe0, 0x1f, 0x29, 0xd0, 0x29, 0xc1, 0x89, 0xca, 0x39, 0xfb, 0x75, 0xd5, 0x81, 0xf9, 0xc1, 0x61, 0xaf, 0x06, 0x74, 0x1d, 0x8b, 0x04, 0x24, 0x8b, 0x00, 0x89, 0x04, 0x24, 0x39, 0x44, 0x24, 0x10, 0x75, 0xa7, 0x83, 0xc4, 0x18, 0x31, 0xc0, 0x5b, 0x5e, 0x5f, 0x5d, 0xc3, 0x8d, 0x74, 0x26, 0x00, 0x90, 0x8b, 0x04, 0x24, 0x8b, 0x78, 0x10, 0x8b, 0x47, 0x3c, 0x89, 0x7c, 0x24, 0x04, 0x8b, 0x5c, 0x07, 0x78, 0x01, 0xfb, 0x8b, 0x73, 0x18, 0x8b, 0x43, 0x20, 0x89, 0x5c, 0x24, 0x14, 0x89, 0x74, 0x24, 0x0c, 0x85, 0xf6, 0x74, 0xbe, 0x01, 0xf8, 0x31, 0xed, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 0x08, 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x44, 0x24, 0x08, 0x8b, 0x5c, 0x24, 0x04, 0x03, 0x1c, 0xa8, 0x8b, 0x44, 0x24, 0x30, 0x85, 0xc0, 0x7e, 0x55, 0x8b, 0x74, 0x24, 0x30, 0xba, 0x49, 0x00, 0x00, 0x00, 0x01, 0xde, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x8d, 0x76, 0x00, 0x0f, 0xb6, 0x0b, 0x83, 0xc3, 0x01, 0x01, 0xd1, 0xc1, 0xe2, 0x05, 0x01, 0xd1, 0x89, 0xc8, 0xf7, 0xe7, 0x89, 0xc8, 0x29, 0xd0, 0xd1, 0xe8, 0x01, 0xc2, 0xc1, 0xea, 0x1e, 0x89, 0xd0, 0xc1, 0xe0, 0x1f, 0x29, 0xd0, 0x29, 0xc1, 0x89, 0xca, 0x39, 0xf3, 0x75, 0xd5, 0x39, 0x54, 0x24, 0x2c, 0x74, 0x1a, 0x83, 0xc5, 0x01, 0x39, 0x6c, 0x24, 0x0c, 0x75, 0x9e, 0xe9, 0x44, 0xff, 0xff, 0xff, 0x90, 0xba, 0x49, 0x00, 0x00, 0x00, 0x39, 0x54, 0x24, 0x2c, 0x75, 0xe6, 0x8b, 0x7c, 0x24, 0x04, 0x8b, 0x74, 0x24, 0x14, 0x8d, 0x04, 0x6f, 0x03, 0x46, 0x24, 0x0f, 0xb7, 0x00, 0x8d, 0x04, 0x87, 0x03, 0x46, 0x1c, 0x03, 0x38, 0x83, 0xc4, 0x18, 0x5b, 0x89, 0xf8, 0x5e, 0x5f, 0x5d, 0xc3, 0x66, 0x90
; With:
db 0x55, 0x57, 0x56, 0x53, 0x83, 0xec, 0x1c, 0x64, 0xa1, 0x30, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 0x10, 0x8b, 0x40, 0x0c, 0x8b, 0x68, 0x14, 0x8d, 0x78, 0x14, 0x89, 0x3c, 0x24, 0x39, 0xef, 0x74, 0x5a, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x5d, 0x28, 0xba, 0x49, 0x00, 0x00, 0x00, 0xbe, 0x03, 0x00, 0x00, 0x00, 0x8d, 0x7b, 0x18, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x90, 0x0f, 0xb6, 0x0b, 0x83, 0xc3, 0x01, 0x01, 0xd1, 0xc1, 0xe2, 0x05, 0x01, 0xd1, 0x89, 0xc8, 0xf7, 0xe6, 0x89, 0xc8, 0x29, 0xd0, 0xd1, 0xe8, 0x01, 0xc2, 0xc1, 0xea, 0x1e, 0x89, 0xd0, 0xc1, 0xe0, 0x1f, 0x29, 0xd0, 0x29, 0xc1, 0x89, 0xca, 0x39, 0xfb, 0x75, 0xd5, 0x81, 0xf9, 0xc1, 0x61, 0xaf, 0x06, 0x74, 0x15, 0x8b, 0x6d, 0x00, 0x39, 0x2c, 0x24, 0x75, 0xad, 0x83, 0xc4, 0x1c, 0x31, 0xc0, 0x5b, 0x5e, 0x5f, 0x5d, 0xc3, 0x8d, 0x76, 0x00, 0x8b, 0x44, 0x24, 0x10, 0x80, 0x78, 0x02, 0x00, 0x75, 0xe1, 0x8b, 0x7d, 0x10, 0x8b, 0x47, 0x3c, 0x89, 0x7c, 0x24, 0x04, 0x8b, 0x5c, 0x07, 0x78, 0x01, 0xfb, 0x8b, 0x73, 0x18, 0x8b, 0x43, 0x20, 0x89, 0x5c, 0x24, 0x14, 0x89, 0x74, 0x24, 0x0c, 0x85, 0xf6, 0x74, 0xbf, 0x01, 0xf8, 0x31, 0xf6, 0x89, 0x6c, 0x24, 0x18, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 0x08, 0x89, 0xf5, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x66, 0x90, 0x8b, 0x44, 0x24, 0x08, 0x8b, 0x5c, 0x24, 0x04, 0x03, 0x1c, 0xa8, 0x8b, 0x44, 0x24, 0x34, 0x85, 0xc0, 0x7e, 0x50, 0x8b, 0x74, 0x24, 0x34, 0xba, 0x49, 0x00, 0x00, 0x00, 0x01, 0xde, 0x66, 0x90, 0x0f, 0xb6, 0x0b, 0x83, 0xc3, 0x01, 0x01, 0xd1, 0xc1, 0xe2, 0x05, 0x01, 0xd1, 0x89, 0xc8, 0xf7, 0xe7, 0x89, 0xc8, 0x29, 0xd0, 0xd1, 0xe8, 0x01, 0xc2, 0xc1, 0xea, 0x1e, 0x89, 0xd0, 0xc1, 0xe0, 0x1f, 0x29, 0xd0, 0x29, 0xc1, 0x89, 0xca, 0x39, 0xf3, 0x75, 0xd5, 0x39, 0x54, 0x24, 0x30, 0x74, 0x1d, 0x83, 0xc5, 0x01, 0x39, 0x6c, 0x24, 0x0c, 0x75, 0xa6, 0x8b, 0x6c, 0x24, 0x18, 0xe9, 0x40, 0xff, 0xff, 0xff, 0xba, 0x49, 0x00, 0x00, 0x00, 0x39, 0x54, 0x24, 0x30, 0x75, 0xe3, 0x8b, 0x7c, 0x24, 0x04, 0x8b, 0x74, 0x24, 0x14, 0x8d, 0x04, 0x6f, 0x03, 0x46, 0x24, 0x0f, 0xb7, 0x00, 0x8d, 0x04, 0x87, 0x03, 0x46, 0x1c, 0x03, 0x38, 0x83, 0xc4, 0x1c, 0x5b, 0x89, 0xf8, 0x5e, 0x5f, 0x5d, 0xc3, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x90

; flag messages
didyou_msg db 'Did you get the flag: ', 0
msg_flag db 'no', 0

; buffer for manipulating
buffer_a:
; 32 random bytes
db 0x43,0xd8,0x8b,0x98,0x47,0x67,0xe1,0x25,0x2,0x74,0xe2,0xe6,0x64,0xd3,0xea,0x0,0x52,0x41,0xf3,0x72,0x8f,0x23,0x71,0x42,0x2,0x8d,0x8,0x7c,0xb2,0xce,0xe,0x88

; buffer for checking
buffer_b:
; (buffer_a xor 0x49) + (32-i):
; [42, 176, 224, 238, 42, 73, 194, 133, 99, 84, 193, 196, 65, 173, 181, 90, 43, 23, 200, 72, 210, 117, 66, 20, 83, 203, 71, 58, 255, 138, 73, 194]
db 0x48,0xc2,0x83,0x85,0x51,0xa,0xf2,0xc1,0x26,0xb,0xf0,0xaa,0x1e,0xd9,0xdd,0x69,0x74,0x55,0xfb,0x7b,0xa2,0x44,0x2c,0x53,0xc,0x83,0x73,0x9,0x9b,0xb9,0x1b,0xbf

xorbuffer_size equ $ - buffer_b

confusing_footer:
  ; [distraction]
  ; just some random stuff
  mov    DWORD  [esp+0x8],3
  call   [0xf00000 + getAddressOfKernel32FunctionByHash + 5]
  mov    DWORD  [esp+0x4], 0xf00000 + buffer_b
  mov    DWORD  [esp],eax
  call [0xf00000 + beep]
  ret

;------

codesize equ $ - code
filesize equ $ - $$

